#include "includes.h"

ShaderObj::ShaderObj() {
    loadExtensions();
	m_programObject = glCreateProgram();
	m_vertCompiled  = GL_FALSE;
	m_fragCompiled  = GL_FALSE;
	m_linked		= GL_FALSE;

}

ShaderObj::~ShaderObj() {


}

/*
 * loadExtensions() - Load OpenGL extensions for anything above OpenGL
 * version 1.1. (This is a requirement from Windows, not from OpenGL.)
 */
int ShaderObj::loadExtensions()
{
    //These extension strings indicate that the OpenGL Shading Language,
    // version 1.00, and GLSL shader objects are supported.
    /*
    if(!glfwExtensionSupported("GL_ARB_shading_language_100"))
    {
        cout<<"GL init error", "GL_ARB_shading_language_100 extension was not found";
        exit(1);
    }
    if(!glfwExtensionSupported("GL_ARB_shader_objects"))
    {
        cout<<"GL sinit error", "GL_ARB_shader_objects extension was not found";
        exit(1);
    }
    else
    {
    */
        glCreateProgram        = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
        glDeleteProgram        = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
        glUseProgram           = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
        glCreateShader         = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
        glDeleteShader         = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
        glShaderSource         = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
        glCompileShader        = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
        glGetShaderiv          = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
        glGetProgramiv         = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
        glAttachShader         = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
        glGetShaderInfoLog     = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
        glGetProgramInfoLog    = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
        glLinkProgram          = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
        glGetUniformLocation   = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
        glUniform4f            = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f");
        glUniform1f            = (PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f");
        glUniform1i            = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i");
        glActiveTextureARB     = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB");
        glMultiTexCoord2fARB   = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress("glMultiTexCoord2fARB");
        glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress("glClientActiveTextureARB");

        if( !glCreateProgram || !glDeleteProgram || !glUseProgram ||
            !glCreateShader || !glDeleteShader || !glShaderSource ||
            !glCompileShader || !glGetProgramiv || !glGetShaderiv ||
            !glAttachShader || !glGetShaderInfoLog || !glGetProgramInfoLog ||
            !glLinkProgram || !glGetUniformLocation || !glUniform4f ||
            !glUniform1f || !glUniform1i )
        {
            cout<<"GL init error", "One or more GL_ARB_shader_objects functions were not found";
            return -1;
        }
    //}
	return 0;
}

bool ShaderObj::loadVertShader( std::string fileName ) {

	GLcharARB *		pInfoLog;
	GLint			maxlength;
	const char *	s;
	int				l;

	std::string			vertCode = "";
	std::string			line;
	std::ifstream		vertFile( fileName.c_str(),std::ios::skipws );

	//vertFile.unsetf( std::ios::skipws );

	while( ! vertFile.eof() ) {

		std::getline( vertFile, line );
		vertCode += line + '\n';

	}

	vertFile.close();

	s = vertCode.c_str();
	l = strlen( s );
	
	m_vertexShaderObject = glCreateShader( GL_VERTEX_SHADER );

	glShaderSource( m_vertexShaderObject, 1, &s, NULL );
	glCompileShader( m_vertexShaderObject );
	glGetShaderiv( m_vertexShaderObject, GL_COMPILE_STATUS, &m_vertCompiled);
	if( m_vertCompiled == GL_FALSE ) {

		glGetShaderInfoLog(m_vertexShaderObject, sizeof(str), NULL, str);
/*
		pInfoLog = new GLcharARB[ maxlength ];
		glGetInfoLogARB( m_vertexShaderObject, maxlength, &l, pInfoLog );

		MessageBox( NULL, pInfoLog, "Vertex Shader Error", MB_OK );
		delete [] pInfoLog;
*/
        cout<<"Vertex shader compile error";
        cout<<str<<endl;
	}
	
	return( m_vertCompiled == GL_TRUE );

}

bool ShaderObj::loadFragShader( std::string fileName ) {

	GLcharARB *		pInfoLog;
	GLint			maxlength;
	const char *	s;
	int				l;

	std::string			fragCode = "";
	std::string			line;
	std::ifstream		fragFile( fileName.c_str() );

	fragFile.unsetf( std::ios::skipws );

	while( ! fragFile.eof() ) {

		std::getline( fragFile, line );
		fragCode += line + '\n';

	}

	fragFile.close();

	s = fragCode.c_str();
	l = strlen( s );
	
	m_fragmentShaderObject = glCreateShader( GL_FRAGMENT_SHADER );

	glShaderSource( m_fragmentShaderObject, 1, &s, NULL );

	glCompileShader( m_fragmentShaderObject );
	glGetShaderiv( m_fragmentShaderObject, GL_COMPILE_STATUS, &m_fragCompiled);
	
	if( m_fragCompiled == GL_FALSE ) {

		glGetShaderInfoLog( m_fragmentShaderObject,	sizeof(str), NULL, str);
/*
		pInfoLog = new GLcharARB[ maxlength ];
		glGetInfoLogARB( m_fragmentShaderObject, maxlength, &l, pInfoLog );

		MessageBox( NULL, pInfoLog, "Fragment Shader Error", MB_OK );
		delete [] pInfoLog;
*/
        cout<<"Fragment shader compile error";
        cout<<str<<endl;

	}
	
	return( m_fragCompiled == GL_TRUE );


}

bool ShaderObj::link() {

	GLint		maxlength, l;
	GLcharARB *	pInfoLog;

	if( m_vertCompiled == GL_FALSE || m_fragCompiled == GL_FALSE ) {

		MessageBox( NULL, "link failed - compile first", "LINK ERROR", MB_OK );
			return( false );
	}

	glAttachShader( m_programObject, m_vertexShaderObject );
	glAttachShader( m_programObject, m_fragmentShaderObject );

	glDeleteShader( m_vertexShaderObject );
	glDeleteShader( m_fragmentShaderObject );

	glLinkProgram( m_programObject );
	glGetProgramiv( m_programObject, GL_LINK_STATUS, &m_linked);

	if ( m_linked != GL_TRUE ){

		glGetProgramInfoLog( m_programObject, sizeof(str), NULL, str );
		/*
		pInfoLog = new GLcharARB [ maxlength ];
		glGetInfoLogARB( m_programObject, maxlength, &l, pInfoLog );
		
		MessageBox( NULL, pInfoLog, "LINK REPORT", MB_OK );
		delete [] pInfoLog;
        */
        cout<<"Program object linking error";
        cout<<str<<endl;
	}

	return( m_linked == GL_TRUE );

}

void ShaderObj::bind() {

	m_nNumTextures = 0;
	glUseProgram( m_programObject );

}

void ShaderObj::unbind() {

	for( int c = m_nNumTextures - 1; c >= 0; c-- ) {

		glActiveTextureARB( GL_TEXTURE0 + c );
		glBindTexture( GL_TEXTURE_2D, 0 );

	}

	glUseProgram( 0 );
}

void ShaderObj::setFloat1Uniform( const char * varName, GLfloat value ) {

	glUniform1f( getUniformLocation( varName ), value );

}


void ShaderObj::setFloat2Uniform( const char * varName, GLfloat val1, GLfloat val2 ) {

	glUniform2fARB( getUniformLocation( varName ), val1, val2 );

}

void ShaderObj::setFloat3Uniform( const char * varName, GLfloat val1, GLfloat val2, GLfloat val3 ) {

	glUniform3fARB( getUniformLocation( varName ), val1, val2, val3 );

}

void ShaderObj::setInt1Uniform( const char * varName, GLint value ) {

	glUniform1i( getUniformLocation( varName ), value );

}

void ShaderObj::setSamplerUniform( const char * varName, GLuint texture ){

	glActiveTextureARB( GL_TEXTURE0 + m_nNumTextures );
	glUniform1i( getUniformLocation( varName ), m_nNumTextures );
	glBindTexture( GL_TEXTURE_2D, texture );
	m_nNumTextures++;

}
void ShaderObj::setSamplerCubeUniform( const char * varName, GLuint texture ){

	glActiveTextureARB( GL_TEXTURE0 + m_nNumTextures );
	glUniform1i( getUniformLocation( varName ), m_nNumTextures );
	glBindTexture( GL_TEXTURE_CUBE_MAP, texture );
	m_nNumTextures++;

}



GLint ShaderObj::getUniformLocation( const char * varName ) {

	return ( glGetUniformLocation( m_programObject, varName ) );

}

void	ShaderObj::setActiveTexture( GLenum unit )
{
    glActiveTextureARB( unit );
}
